/**
* AutoSWF - Cody Cushing. March 26, 2009
* Copyright (c) 2009 Cody Cushing
* No use restrictions. Maintain this notice in all versions. 
**/

package com.autoswf.core
{
	import flexunit.framework.TestCase;
	
	import flash.errors.IllegalOperationError;

	public class LinkTest extends TestCase
	{
		private var link:Link;
		private var mockLink1:MockLink;
		private var mockLink2:MockLink;

		public function LinkTest(methodName:String=null)
		{
			super(methodName);
		}
		
		override public function setUp():void
		{
			mockLink1 = new MockLink("mockLink1");
			mockLink2 = new MockLink("mockLink2")
			link = new Link("testLink");
		}
		
		
		/** All link have string ids. */
		public function testGetId():void
		{
			assertEquals("cannot access id", "testLink", link.id);
		}
		
		
		/** toString prints the link's id */
		public function testToString():void
		{
			assertEquals("toString did not resolve to id", "testLink", String(link));
		}
		
		
		/** A reference to a parent link can be removed */
		public function testRemoveParentLink():void
		{
			link.addParentLink(mockLink1);
			link.removeParentLink(mockLink1);
			link.sendUp("message");
			assertNull("message resolved to removed parent", mockLink1.sendUpResult);
		}
		
		
		/** Adding 2 links with the same id or adding the same link twice throws an error */
		public function testAddChildIdCollision():void
		{
			var mockLink1:MockLink = new MockLink("mockLink");
			var mockLink2:MockLink = new MockLink("mockLink");
			link.addChildLink(mockLink1);
			
			var flag:Boolean = false;
			try
			{
				link.addChildLink(mockLink2);
			}
			catch(error:IllegalOperationError)
			{
				flag = true;
			}
			assertTrue("child id collision did not throw error", flag);
		}
		
		
		/** A child link can be removed */
		public function testRemoveChildLink():void
		{
			link.addChildLink(mockLink1);
			link.removeChildLink(mockLink1)
			link.sendDown(new Transport().setPath("mockLink1->message"));
			assertNull("message resolved to removed child", mockLink1.sendDownResult);
		}
		
		
		/** sendUp passes an object to parent links */
		public function testSendUp():void
		{
			link.addParentLink(mockLink1);
			
			link.sendUp("message");
			assertEquals("child did not prepend its id", "testLink->message", mockLink1.sendUpResult);			
			assertEquals("child did not prepend its id", "message", Transport(mockLink1.sendUpResult).message);			
		}
		
		
		/** sendDown passes an object to a specific child link */
		public function testSendDown01():void
		{
			link.addChildLink(mockLink1);
			link.addChildLink(mockLink2);
			
			link.sendDown(new Transport().setPath("message"));
			assertNull("no valid id, but request resolved to child", mockLink1.sendDownResult);
		}
		
		
		/** sendDown passes an object to a specific child link */
		public function testSendDown02():void
		{
			link.addChildLink(mockLink1);
			link.addChildLink(mockLink2);
			
			link.sendDown(new Transport("message1").setPath("mockLink1"));
			assertEquals("correct id, but message was not passed", "message1", mockLink1.sendDownResult);
			assertNull("message resolved to wrong child", mockLink2.sendDownResult);			
		}
		
		
		/** sendDown can retrieve public properties from itself */		
		public function testAccessProperty01():void
		{
			assertEquals("unable to access public property", "testLink", 
			    link.sendDown(new Transport().setPath("id")));
		}
		
		
		/** sendDown can not retrieve private properties from itself */		
		public function testAccessProperty02():void
		{
			link.client = this;
			assertFalse("able to access private property", link.sendDown(new Transport().setPath("_client")) == this);
		}
		
		
		/** sendDown can retrieve public properties from its client object */
		public function testAccessPropertyOnClient():void
		{
			var testClient:Object = {};
			testClient.prop = "value";
			link.client = testClient;
			
			assertEquals("failed to retrieve property on client object", "value", 
			    link.sendDown(new Transport().setPath("prop")));
		}
		
		
		/** sendDown can invoke public methods on its client object */
		public function testInvokeMethod01():void
		{
			var flag:Boolean = false;
			var testClient:Object = {};
			testClient.func = function():void
			{
				flag = true;
			}
			link.client = testClient;
			link.sendDown(new Transport().setPath("func"));
			assertTrue("failed to invoke function on client", flag);
		}
		
		
		/** sendDown can invoke public methods on its client object and apply arguments */
		public function testInvokeMethod02():void
		{
			var testClient:Object = {};
			testClient.func = function(arg1:Object, arg2:Object, arg3:Object):void
			{
				assertEquals(arg1 + arg2 + arg3, "abc");
			}
			link.client = testClient;
			link.sendDown(new Transport().setPath("func").setArguments(["a", "b", "c"]));
		}
		
		
		/** sendDown can access public properties on a subclass */
		public function testAccessPropertyOnSubclass():void
		{
			var subclass:LinkSubclass = new LinkSubclass("subclassId");
			assertEquals("failed to retrieve property on subclass", "testVal", 
			    subclass.sendDown(new Transport().setPath("testProperty")));
		}
		
		
		/** sendDown can invoke functions on a subclass */
		public function testAccessFunctionOnSubclass():void
		{
			var subclass:LinkSubclass = new LinkSubclass("subclassId");
			assertEquals("failed to retrieve property on subclass", "functionResult", 
			    subclass.sendDown(new Transport().setPath("testFunction")));
		}
		
		
		/** sendUp logic flow can be hooked into */
		public function testHook():void
		{
			var obj:Object = {}
			link.hook = function(transport:Transport):void
			{
				assertStrictlyEquals("", transport.message, obj);
			}
			
			link.sendUp(obj);
		}
		
	}
}
